<?php

// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2011 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: luofei614 <weibo.com/luofei614>
// +----------------------------------------------------------------------
// | 修改者: anuo (本权限类在原3.2.3的基础上修改过来的)
// +----------------------------------------------------------------------
namespace baofu;


//include_once EXTEND_PATH.'/baofu/SecurityUtil/AESUtil.php';
//include_once EXTEND_PATH.'/baofu/SecurityUtil/RSAUtil.php';
//include_once EXTEND_PATH.'/baofu/SecurityUtil/SignatureUtils.php';
//include_once EXTEND_PATH.'/baofu/SecurityUtil/SHA1Util.php';
//include_once EXTEND_PATH.'/baofu/Util/Log.php';
//include_once EXTEND_PATH.'/baofu/Util/HttpClient.php';
//include_once EXTEND_PATH.'/baofu/Util/Tools.php';

use baofu\SecurityUtil\AESUtil;
use baofu\SecurityUtil\RSAUtil;
use baofu\SecurityUtil\SignatureUtils;
use baofu\SecurityUtil\SHA1Util;
use baofu\Util\Log;
use baofu\Util\HttpClient;
use baofu\Util\Tools;
use think\Exception;

class Baofu
{

    public $Version = "4.0.0.0";//版本号
    public $Member_Id = "100029078";    //商户号
    public $Terminal_Id = "200004282";    //终端号

    public $KEY_PWD = "123456";    //商户私钥证书密码

    public $KeyFolder = EXTEND_PATH . '/baofu/CerFile/';    //证书路径
    public $PFX_FILE_PATH = "bfkey_100029078@@200004282.pfx";  //注意证书路径是否存在
    public $CER_FILE_PATH = "bfkey_100029078@@200004282.cer";//注意证书路径是否存在

    public $POST_URL = "https://vgw.baofoo.com/cutpayment/protocol/backTransRequest";  //测试环境请求地址

    //https://vgw.baofoo.com/cutpayment/api/backTransRequest
    public $AesKey = '4f66405c4f66405c'; //商户自定义（可随机生成  商户自定义(AES key长度为=16位)）

    public $GLOBALS = [];

    public function __construct($config = [])
    {
        $this->PFX_FILE_PATH = $this->KeyFolder . $this->PFX_FILE_PATH;
        $this->CER_FILE_PATH = $this->KeyFolder . $this->CER_FILE_PATH;

        if(!empty($config)){
            $this->Version = $config['baofu_version'];
            $this->Member_Id = $config['baofu_member_Id'];
            $this->Terminal_Id = $config['baofu_terminal_Id'];
            $this->KEY_PWD = $config['baofu_key_pwd'];
            $this->PFX_FILE_PATH = ROOT_PATH.'/public/'.$config['baofu_pfx'];
            $this->CER_FILE_PATH = ROOT_PATH.'/public/'.$config['baofu_cer'];
            $this->POST_URL = $config['baofu_url'];
            $this->AesKey = $config['baofu_AesKey'];
        }

        $this->GLOBALS = [
            'Version' => $this->Version,
            'Member_Id' => $this->Member_Id,
            'Terminal_Id' => $this->Terminal_Id,
            'KEY_PWD' => $this->KEY_PWD,
            'PFX_FILE_PATH' => $this->PFX_FILE_PATH,
            'CER_FILE_PATH' => $this->CER_FILE_PATH,
            'POST_URL' => $this->POST_URL,
        ];
    }

    public function dgtl_envlp(){
        try {
            Log::LogWirte('dgtl_envlp-开始');//记录日志
            $dgtl_envlp = "01|" . $this->AesKey;//使用接收方的公钥加密后的对称密钥，并做Base64转码，明文01|对称密钥，01代表AES[密码商户自定义]
            Log::LogWirte('dgtl_envlp-'.$dgtl_envlp);//记录日志
            Log::LogWirte('dgtl_envlp-'.$this->GLOBALS["CER_FILE_PATH"]);//记录日志
            return RSAUtil::encryptByCERFile($dgtl_envlp, $this->GLOBALS["CER_FILE_PATH"]);//公钥加密
        }catch (Exception $exception){
            Log::LogWirte('error-'.$exception->getMessage());//记录日志
        }
    }

    public function getDateAry($arr = []){
        $DateArry = array();
        $DateArry['msg_id'] = $this->getMsgID();
        $DateArry["send_time"] = Tools::getTime();
        $DateArry["version"] = $this->GLOBALS["Version"];
        $DateArry["terminal_id"] = $this->GLOBALS["Terminal_Id"];
        $DateArry["member_id"] = $this->GLOBALS["Member_Id"];
        $DateArry['msg_id'] = $this->getMsgID();
        return $DateArry;
    }

    public function getMsgID(){
        return "TISN" . Tools::getTransid() . Tools::getRand4();
    }

    //商户订单号
    public function getTransID(){
        return "TID" . Tools::getTransid() . Tools::getRand4();
    }

    //退款流水号
    public function getTransSerialNo(){
        return "RTSN" . Tools::getTransid() . Tools::getRand4();
    }

    /**
     * readySign 预绑卡接口
     * @param $Cardinfo string      账户信息[银行卡号|持卡人姓名|证件号|手机号|银行卡安全码|银行卡有效期] , 例如： 6222020111122220000|张宝|340101198108119852|18689262776||
     * @param $user_id string       用户在商户平台唯一ID
     * @param $card_type string     卡类型  101    借记卡
     * @return void
     * @throws \think\Exception
     */
    public function readySign($Cardinfo = '', $data = [])
    {

        $CardinfoAES = AESUtil::AesEncrypt(base64_encode($Cardinfo), $this->AesKey);//先BASE64后进行AES加密


        Log::LogWirte('预绑卡-开始');//记录日志
        \think\Log::write('预绑卡-开始');

        $DateArry = $this->getDateAry();
        $DateArry["txn_type"] = "01";//交易类型
        $DateArry["card_type"] = "101";//卡类型  101	借记卡，102 信用卡
        $DateArry["id_card_type"] = "01";//证件类型
        $DateArry['dgtl_envlp'] = $this->dgtl_envlp(); //公钥加密
        $DateArry['acc_info'] = $CardinfoAES;

        $DateArry = array_merge($DateArry,$data);


        $SignVStr = Tools::SortAndOutString($DateArry);//签名字段格式化
        $SHA1Sign = SHA1Util::Sha1AndHex(urldecode($SignVStr));//签名(注意此处urldecode方法主要是去除send_time中间的空格和符号的自动自urlencode)

        $Sign = SignatureUtils::Sign($SHA1Sign, $this->GLOBALS["PFX_FILE_PATH"], $this->GLOBALS["KEY_PWD"]);
        $DateArry["signature"] = $Sign;//签名域

        $PostString = HttpClient::Post($DateArry, $this->GLOBALS["POST_URL"]);


        if($PostString){
            parse_str($PostString, $ReturnData);//参数解析

            if(isset($ReturnData["signature"])){
                $RSign = $ReturnData["signature"];//存储返回签名做后续验签使用。
                $RSignArray = Tools::array_remove($ReturnData, "signature");//需要删除签名字段

                $RSignVStr = Tools::SortAndOutString($RSignArray);
                $RSignature = SHA1Util::Sha1AndHex(urldecode($RSignVStr));

                if (SignatureUtils::VerifySign($RSignature, $this->GLOBALS["CER_FILE_PATH"], $RSign)) {

                    Log::LogWirte('预绑卡-格式化数据'.json_encode($DateArry,JSON_UNESCAPED_UNICODE ));//记录日志
                    \think\Log::write('预绑卡-格式化数据'.json_encode($DateArry,JSON_UNESCAPED_UNICODE ));

                    if (isset($RSignArray['resp_code']) && isset($RSignArray['dgtl_envlp']) && $RSignArray["resp_code"] == "S") {

                        $RDgtlEnvlp = RSAUtil::decryptByPFXFile($RSignArray["dgtl_envlp"], $this->GLOBALS["PFX_FILE_PATH"], $this->GLOBALS["KEY_PWD"]);
                        $RAesKey = Tools::getAesKey($RDgtlEnvlp);//获取返回的AESkey
                        $UniqueCode = base64_decode(AESUtil::AesDecrypt($RSignArray["unique_code"], $RAesKey));
                        //Log::EchoFormat("唯一码:".$UniqueCode);
                        Log::LogWirte('预绑卡-成功');//记录日志
                        return ['code' => 200, 'data' => ['unique_code' => $UniqueCode]];
                    }
                }
            }
        }
        Log::LogWirte('预绑卡-失败-'.$PostString);//记录日志
        \think\Log::LogWirte('预绑卡-失败-'.$PostString);//记录日志
        return ['code' => 0];
    }



    /**
     * 确认绑卡
     * @param $UniqueCode 预签约唯一码(预绑卡返回的值)[格式：预签约唯一码|短信验证码]
     * @return array|void
     * @throws \think\Exception
     */
    public function confirmSign($UniqueCode = '')
    {
        Log::LogWirte('确认绑卡');//记录日志
        \think\Log::write('确认绑卡');

        $dgtl_envlp = $this->dgtl_envlp();//公钥加密
        $UniqueCodeAES = AESUtil::AesEncrypt(base64_encode($UniqueCode), $this->AesKey);//先BASE64后进行AES加密

        $DateArry = $this->getDateAry();
        $DateArry["txn_type"] = "02";//交易类型
        $DateArry["dgtl_envlp"] = $dgtl_envlp;
        $DateArry["unique_code"] = $UniqueCodeAES;//预签约唯一码


        $SignVStr = Tools::SortAndOutString($DateArry);//签名字段格式化
        $SHA1Sign = SHA1Util::Sha1AndHex(urldecode($SignVStr));//签名(注意此处urldecode方法主要是去除send_time中间的空格和符号的自动自urlencode)

        $Sign = SignatureUtils::Sign($SHA1Sign, $this->GLOBALS["PFX_FILE_PATH"], $this->GLOBALS["KEY_PWD"]);
        $DateArry["signature"] = $Sign;//签名域

        $PostString = HttpClient::Post($DateArry, $this->GLOBALS["POST_URL"]);

        if($PostString){
            parse_str($PostString, $ReturnData);//参数解析

            if(isset($ReturnData["signature"])){
                $RSign = $ReturnData["signature"];//存储返回签名做后续验签使用。
                $RSignArray = Tools::array_remove($ReturnData, "signature");//需要删除签名字段

                $RSignVStr = Tools::SortAndOutString($RSignArray);
                $RSignature = SHA1Util::Sha1AndHex(urldecode($RSignVStr));

                if (SignatureUtils::VerifySign($RSignature, $this->GLOBALS["CER_FILE_PATH"], $RSign)) {
                    if (isset($RSignArray['resp_code']) && isset($RSignArray['dgtl_envlp'])) {

                        Log::LogWirte('确认绑卡-'.json_encode($DateArry,JSON_UNESCAPED_UNICODE ));//记录日志
                        \think\Log::write('确认绑卡-'.json_encode($DateArry,JSON_UNESCAPED_UNICODE ));

                        if($RSignArray["resp_code"] == "S"){
                            $RDgtlEnvlp = RSAUtil::decryptByPFXFile($RSignArray["dgtl_envlp"], $this->GLOBALS["PFX_FILE_PATH"], $this->GLOBALS["KEY_PWD"]);
                            $RAesKey = Tools::getAesKey($RDgtlEnvlp);//获取返回的AESkey
                            $UniqueCode = base64_decode(AESUtil::AesDecrypt($RSignArray["protocol_no"], $RAesKey));
                            return [
                                'code' => 200,
                                'data' => [
                                    'protocol_no' => explode('|',$UniqueCode)[0], //$UniqueCode,
                                ]
                            ];
                        }
                        if($RSignArray["resp_code"] == "I"){
                            Log::LogWirte('用户绑卡处理中');//记录日志
                            return ['code' => 201];
                        }
                    }
                }
            }
        }
        Log::LogWirte('确认绑卡-失败');//记录日志
        \think\Log::write('确认绑卡-失败');

        return ['code' => 0];
    }

    public function getDateArry(){

        $dgtl_envlp = $this->dgtl_envlp();

        $DateArry = array();
        $DateArry["send_time"] = Tools::getTime();
        $DateArry["msg_id"] = "TISN" . Tools::getTransid() . Tools::getRand4();//报文流水号
        $DateArry["version"] = $this->GLOBALS["Version"];
        $DateArry["terminal_id"] = $this->GLOBALS["Terminal_Id"];
        $DateArry["member_id"] = $this->GLOBALS["Member_Id"];
        $DateArry["dgtl_envlp"] = $dgtl_envlp;

        return $DateArry;
    }

    //查询绑卡
    public function queryBind($AccNo = '6222020111122220000',$user_id)
    {
        Log::LogWirte('开始查询用户绑卡');//记录日志
        $AccNoAES = AESUtil::AesEncrypt(base64_encode($AccNo), $this->AesKey);//先BASE64后进行AES加密

        $DateArry = $this->getDateArry();

        $data = [
            'txn_type' => "03",
            'user_id' => $user_id,
            'acc_no' => $AccNoAES
        ];
        $DateArry = array_merge($DateArry,$data);


        //Log::EchoFormat("报文流水号:".$DateArry["msg_id"]);
        $SignVStr = Tools::SortAndOutString($DateArry);//签名字段格式化
        //Log::EchoFormat("SHA-1摘要明文字串：".$SignVStr);
        $SHA1Sign = SHA1Util::Sha1AndHex(urldecode($SignVStr));//签名(注意此处urldecode方法主要是去除send_time中间的空格和符号的自动自urlencode)
        //Log::EchoFormat("SHA-1摘要结果：".$SHA1Sign);

        $Sign = SignatureUtils::Sign($SHA1Sign, $this->GLOBALS["PFX_FILE_PATH"], $this->GLOBALS["KEY_PWD"]);
        //Log::EchoFormat("RSA签名结果：".$Sign);
        $DateArry["signature"] = $Sign;//签名域

        $PostString = HttpClient::Post($DateArry, $this->GLOBALS["POST_URL"]);

        if($PostString){
            parse_str($PostString, $ReturnData);//参数解析

            if(isset($ReturnData["signature"])){
                $RSign = $ReturnData["signature"];//存储返回签名做后续验签使用。
                $RSignArray = Tools::array_remove($ReturnData, "signature");//需要删除签名字段

                $RSignVStr = Tools::SortAndOutString($RSignArray);
                $RSignature = SHA1Util::Sha1AndHex(urldecode($RSignVStr));

                if (SignatureUtils::VerifySign($RSignature, $this->GLOBALS["CER_FILE_PATH"], $RSign)) {
                    if (isset($RSignArray['resp_code']) && isset($RSignArray['dgtl_envlp'])) {
                        if($RSignArray["resp_code"] == "S"){
                            $RDgtlEnvlp = RSAUtil::decryptByPFXFile($RSignArray["dgtl_envlp"], $this->GLOBALS["PFX_FILE_PATH"], $this->GLOBALS["KEY_PWD"]);
                            $RAesKey = Tools::getAesKey($RDgtlEnvlp);//获取返回的AESkey
                            $UniqueCode = base64_decode(AESUtil::AesDecrypt($RSignArray["protocols"], $RAesKey));
                            Log::LogWirte('查询用户绑卡-成功');//记录日志
                            return [
                                'code' => 200,
                                'data' => [
                                    'protocol_no' => $UniqueCode,
                                ]
                            ];
                        }
                        if($RSignArray["resp_code"] == "I"){
                            Log::LogWirte('查询用户绑卡-处理中');//记录日志
                            return ['code' => 201];
                        }
                    }
                }
            }
        }
        Log::LogWirte('查询用户绑卡-绑卡失败');//记录日志
        return ['code' => 0];
    }

    public function queryPay($data = []){

        Log::LogWirte('查询支付');//记录日志

        $DateArry = $this->getDateArry();
        $DateArry["txn_type"]= "07";//交易类型
        $DateArry["orig_trade_date"]= Tools::getTime();//
        $DateArry["orig_trans_id"]=$data['trans_id'];//交易时的trans_id


        $SignVStr =  Tools::SortAndOutString($DateArry);//签名字段格式化
        $SHA1Sign = SHA1Util::Sha1AndHex(urldecode($SignVStr));//签名(注意此处urldecode方法主要是去除send_time中间的空格和符号的自动自urlencode)

        $Sign = SignatureUtils::Sign($SHA1Sign, $this->GLOBALS["PFX_FILE_PATH"],$this->GLOBALS["KEY_PWD"]);
        $DateArry["signature"]=$Sign;//签名域

        $PostString  =  HttpClient::Post($DateArry, $this->GLOBALS["POST_URL"]);

        if(!empty($PostString)){
            parse_str($PostString,$ReturnData);//参数解析

            if(array_key_exists("signature",$ReturnData)){
                $RSign=$ReturnData["signature"];//存储返回签名做后续验签使用。
                $RSignArray = Tools::array_remove($ReturnData, "signature");//需要删除签名字段
                $RSignVStr =Tools::SortAndOutString($RSignArray);

                //签名(注意此处urldecode方法主要是去除send_time中间的空格和符号的自动自urlencode)
                $RSignature = SHA1Util::Sha1AndHex(urldecode($RSignVStr));

                if(SignatureUtils::VerifySign($RSignature,$GLOBALS["CER_FILE_PATH"],$RSign)){

                    Log::LogWirte('查询支付结束-'.json_encode($PostString));

                    if(array_key_exists("resp_code",$RSignArray)){
                        if($RSignArray["resp_code"] == "S"){
                            return ['code' => 200,'notify' => $RSignArray];
                        }else if($RSignArray["resp_code"] == "I"){
                            return ['code' => 201,'notify' => $RSignArray];
                        }else if($RSignArray["resp_code"] =="F"){
                            return ['code' => 0,'notify' => $RSignArray];
                        }
                    }
                }
            }
        }
        Log::LogWirte('查询支付结束-'.$PostString);//记录日志
        return ['code' => 0,'notify' => $PostString];
    }

    /**
     * 解除绑卡
     * @param $ProtocolNo 签约协议号
     * @param $user_id  用户平台唯一id
     * @return void
     * @throws \think\Exception
     */
    public function ABolishBind($ProtocolNo = '1202306281451538580008246542',$user_id = '')
    {
        $dgtl_envlp = $this->dgtl_envlp(); //公钥加密
        $ProtocolNoAES = AESUtil::AesEncrypt(base64_encode($ProtocolNo), $this->AesKey);//先BASE64后进行AES加密
        Log::LogWirte('用户解除绑卡');//记录日志

        $DateArry = array();
        $DateArry["send_time"] = Tools::getTime();
        $DateArry["msg_id"] = "TISN" . Tools::getTransid() . Tools::getRand4();//报文流水号
        $DateArry["version"] = $this->GLOBALS["Version"];
        $DateArry["terminal_id"] = $this->GLOBALS["Terminal_Id"];
        $DateArry["txn_type"] = "04";//交易类型
        $DateArry["member_id"] = $this->GLOBALS["Member_Id"];
        $DateArry["dgtl_envlp"] = $dgtl_envlp;
        $DateArry["user_id"] = $user_id;//用户在商户平台唯一ID
        $DateArry["protocol_no"] = $ProtocolNoAES;//签约协议号（密文）


        $SignVStr = Tools::SortAndOutString($DateArry);//签名字段格式化
        $SHA1Sign = SHA1Util::Sha1AndHex(urldecode($SignVStr));//签名(注意此处urldecode方法主要是去除send_time中间的空格和符号的自动自urlencode)

        $Sign = SignatureUtils::Sign($SHA1Sign, $this->GLOBALS["PFX_FILE_PATH"], $this->GLOBALS["KEY_PWD"]);
        $DateArry["signature"] = $Sign;//签名域

        $PostString = HttpClient::Post($DateArry, $this->GLOBALS["POST_URL"]);

        if($PostString){
            parse_str($PostString, $ReturnData);//参数解析

            if(isset($ReturnData["signature"])){
                $RSign = $ReturnData["signature"];//存储返回签名做后续验签使用。
                $RSignArray = Tools::array_remove($ReturnData, "signature");//需要删除签名字段

                $RSignVStr = Tools::SortAndOutString($RSignArray);
                $RSignature = SHA1Util::Sha1AndHex(urldecode($RSignVStr));

                if (SignatureUtils::VerifySign($RSignature, $this->GLOBALS["CER_FILE_PATH"], $RSign)) {
                    if (isset($RSignArray['resp_code']) && $RSignArray["resp_code"] == "S") {

                        Log::LogWirte('用户解除绑卡成功');//记录日志
                        return ['code' => 200];
                    }
                }
            }
        }
        Log::LogWirte('用户解除绑卡失败');//记录日志
        return ['code' => 0];
    }


    /**
     * 直接支付接口
     * @param $ProtocolNo 签约协议号
     * @param $user_id 用户id
     * @param $txn_amt 支付金额, 分
     * @param $RiskItem 风控参数
     * @return void
     * @throws \think\Exception
     */
    public function singlePay($data = [])
    {
        Log::LogWirte('开始支付');//记录日志
        $dgtl_envlp = $this->dgtl_envlp();
        $ProtocolNoAES = AESUtil::AesEncrypt(base64_encode($data['protocol_no']), $this->AesKey);//先BASE64后进行AES加密

        Log::LogWirte('数据解密-'.$ProtocolNoAES);//记录日志

        $ReturnUrl = "";//异步通知地址。

        $DateArry = $this->getDateAry();
        $DateArry["send_time"] = Tools::getTime();
        $DateArry["txn_type"] = "08";//交易类型
        $DateArry["dgtl_envlp"] = $dgtl_envlp;
        $DateArry["card_info"] = "";//卡信息: 【信用卡：信用卡有效期|安全码,借记卡：传空】
        $DateArry["return_url"]=$ReturnUrl;//最多填写三个地址,不同地址用间使用‘|’分隔
        $DateArry = array_merge($DateArry,$data);
        $ProtocolNoAES = AESUtil::AesEncrypt(base64_encode($data['protocol_no']), $this->AesKey);
        $DateArry["protocol_no"]=$ProtocolNoAES;//签约协议号（密文）

        $SignVStr =  Tools::SortAndOutString($DateArry);//签名字段格式化
        $SHA1Sign = SHA1Util::Sha1AndHex(urldecode($SignVStr));//签名(注意此处urldecode方法主要是去除send_time中间的空格和符号的自动自urlencode)

        $Sign = SignatureUtils::Sign($SHA1Sign, $this->GLOBALS["PFX_FILE_PATH"],$this->GLOBALS["KEY_PWD"]);
        $DateArry["signature"]=$Sign;//签名域


        Log::LogWirte('数据发送前-'.$ProtocolNoAES);//记录日志

        $PostString = HttpClient::Post($DateArry, $this->GLOBALS["POST_URL"]);
        if($PostString){
            parse_str($PostString, $ReturnData);//参数解析

            if(isset($ReturnData["signature"])){
                $RSign = $ReturnData["signature"];//存储返回签名做后续验签使用。
                $RSignArray = Tools::array_remove($ReturnData, "signature");//需要删除签名字段

                $RSignVStr = Tools::SortAndOutString($RSignArray);
                $RSignature = SHA1Util::Sha1AndHex(urldecode($RSignVStr));

                if (SignatureUtils::VerifySign($RSignature, $this->GLOBALS["CER_FILE_PATH"], $RSign)) {

                    if (isset($RSignArray['resp_code'])) {
                        if($RSignArray["resp_code"] == "S"){
                            //Log::EchoFormat("唯一码:".$UniqueCode);
                            Log::LogWirte('直接支付成功');//记录日志
                            \think\Log::write('直接支付成功-'.$data['user_id']);
                            return ['code' => 200,'data' => $DateArry,'notify' => $RSignArray];
                        }elseif($RSignArray["resp_code"] == "I"){
                            Log::LogWirte('直接支付处理中');//记录日志
                            \think\Log::write('直接支付处理中-'.$data['user_id']);
                            return ['code' => 201,'data' => $DateArry,'notify' => $RSignArray];
                        }
                    }
                }
                return ['code' => 0,'notify' => $RSignArray];
            }
        }
        \think\Log::write('直接支付失败-'.$data['user_id']);
        Log::LogWirte('直接支付失败');//记录日志
        return ['code' => 0,'notify' => ''];
    }

}